package br.ufpe.cin.imlm2.util.options;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class Options implements IOptions {

	private static final String LIST_SEPARATOR = ";";
	protected List<Option> options;

	public Options() {
		this.options = new ArrayList<Option>();
	}

	protected static List<Integer> getIntListOption( String toBeParsed ) {
		List<Integer> intList = new ArrayList<Integer>();
		for ( String part : toBeParsed.split( LIST_SEPARATOR ) ) {
			intList.add( Integer.parseInt( part ) );
		}
		return intList;
	}

	protected static List<String> getStringListOption( String toBeParsed ) {
		List<String> strList = new ArrayList<String>();
		if ( !toBeParsed.isEmpty() ) {
			for ( String part : toBeParsed.split( LIST_SEPARATOR ) ) {
				strList.add( part );
			}
		}
		return strList;
	}

	protected static List<Float> getFloatListOption( String toBeParsed ) {
		List<Float> floatList = new ArrayList<Float>();
		for ( String part : toBeParsed.split( LIST_SEPARATOR ) ) {
			floatList.add( Float.parseFloat( part ) );
		}
		return floatList;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.ufpe.cin.imlm2.util.IOptions#parse(java.util.Map)
	 */
	@Override
	public void parse( Map<String, String> options ) throws ParsingException, NoSuchOptionException {
		for ( Entry<String, String> entry : options.entrySet() ) {
			Option opt = null;
			try {
				if ( (opt = this.getOptionInternal( entry.getKey() )) != null ) {
					Class<?> type = opt.getType();
					if ( type.equals( String.class ) ) {
						opt.setValue( entry.getValue() );
					} else if ( type.equals( Integer.class ) ) {
						opt.setValue( Integer.parseInt( entry.getValue() ) );
					} else if ( type.equals( Float.class ) ) {
						opt.setValue( Float.parseFloat( entry.getValue() ) );
					} else if ( type.equals( Boolean.class ) ) {
						opt.setValue( Boolean.parseBoolean( entry.getValue() ) );
					} else if ( type.equals( List.class ) ) {
						type = opt.getComponenType();
						if ( type.equals( String.class ) ) {
							opt.setValue( getStringListOption( entry.getValue() ) );
						} else if ( type.equals( Integer.class ) ) {
							opt.setValue( getIntListOption( entry.getValue() ) );
						} else if ( type.equals( Float.class ) ) {
							opt.setValue( getFloatListOption( entry.getValue() ) );
						}
					}

				} else
					throw new NoSuchOptionException( entry.getKey(),
							"There is no option with name " + entry.getKey() );

			} catch ( InvalidValueException e ) {
				throw new ParsingException( "Invalid value " + e.getValue() + " for option "
						+ opt.getName(), e );
			}
		}
	}

	protected Option getOptionInternal( String possibleName ) {
		for ( Option o : this.options ) {
			if ( o.getName().equals( possibleName ) || o.getAlias().equals( possibleName ) )
				return o;
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.ufpe.cin.imlm2.util.IOptions#getDeclaredOptions()
	 */
	@Override
	public List<String> getDeclaredOptions() {
		List<String> declaredOpts = new ArrayList<String>();
		for ( Option o : this.options ) {
			declaredOpts.add( o.getName() );
		}
		return declaredOpts;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.ufpe.cin.imlm2.util.IOptions#getOptionValue(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public String getOptionStringValue( String optName ) throws NoSuchOptionException {
		Option opt;
		if ( (opt = this.getOptionInternal( optName )) != null ) {
			if ( !opt.getType().equals( List.class ) )
				return opt.getValue().toString();
			else {
				StringBuffer buffer = new StringBuffer();
				for ( Object obj : (List<Object>) opt.getValue() ) {
					buffer.append( obj.toString() ).append( LIST_SEPARATOR );
				}
				if ( buffer.length() > 0 )
					buffer.deleteCharAt( buffer.length() - 1 );
				return buffer.toString();
			}

		}

		throw new NoSuchOptionException( optName, "There is not option with name " + optName );
	}

	@Override
	public boolean addOption( Option op ) {
		if ( !this.options.contains( op ) ) {
			this.options.add( op );
			return true;
		}
		return false;
	}

	@Override
	public List<Option> getOptions() {
		return new ArrayList<Option>( this.options );
	}

	@Override
	public Option getOption( String optName ) throws NoSuchOptionException {
		Option o = this.getOptionInternal( optName );
		if ( o != null )
			return o;
		throw new NoSuchOptionException( optName, "There is not option with name " + optName );
	}

}
